const axios = require('axios');
const Stock = require('../models/stock');
const StockTools = require('./tools');

class StockService {
  // 获取股票数据的API
  static async fetchStockData(stockCode) {
    try {
      // 使用腾讯股票API获取实时数据
      // 根据股票代码格式化为腾讯API需要的格式
      // 深市股票代码前加sz，沪市股票代码前加sh
      let formattedCode = stockCode;
      if (!stockCode.startsWith('sz') && !stockCode.startsWith('sh')) {
        // 简单判断股票代码类型
        if (stockCode.startsWith('0') || stockCode.startsWith('3') || stockCode.startsWith('1')) {
          formattedCode = `sz${stockCode}`;
        } else if (stockCode.startsWith('6') || stockCode.startsWith('5')) {
          formattedCode = `sh${stockCode}`;
        }
      }
      
      // 使用tools.js中的get方法获取股票数据
      const stockData = await StockTools.get(formattedCode, null, null);
      
      // 如果是交易时间结束后，设置收盘价
      const now = new Date();
      const hour = now.getHours();
      if (hour >= 15) { // 假设15点后交易结束
        stockData.closePrice = stockData.price;
      }
      
      return stockData;
    } catch (error) {
      console.error(`获取股票 ${stockCode} 数据失败:`, error);
      
      // 如果API获取失败，使用备用的模拟数据（仅用于开发测试）
      console.log(`使用模拟数据作为备用 - 股票代码: ${stockCode}`);
      return this.generateMockData(stockCode);
    }
  }
  
  // 生成模拟数据（仅用于API获取失败时的备用方案）
  static generateMockData(stockCode) {
    const mockData = {
      code: stockCode,
      name: `股票${stockCode}`,
      price: parseFloat((Math.random() * 100 + 50).toFixed(2)),
      openPrice: parseFloat((Math.random() * 100 + 50).toFixed(2)),
      closePrice: null, // 收盘价在交易日结束时才有
      highPrice: parseFloat((Math.random() * 120 + 60).toFixed(2)),
      lowPrice: parseFloat((Math.random() * 80 + 40).toFixed(2)),
      timestamp: new Date()
    };
    
    // 确保最高价大于开盘价，最低价小于开盘价
    mockData.highPrice = Math.max(mockData.highPrice, mockData.openPrice);
    mockData.lowPrice = Math.min(mockData.lowPrice, mockData.openPrice);
    
    // 如果是交易时间结束后，设置收盘价
    const now = new Date();
    const hour = now.getHours();
    if (hour >= 15) { // 假设15点后交易结束
      mockData.closePrice = parseFloat((Math.random() * 100 + 50).toFixed(2));
    }
    
    return mockData;
  }

  // 更新所有股票数据
  // 股票数据缓存
  static stockCache = {
    data: new Map(), // 存储股票数据
    lastUpdateTime: null, // 上次更新时间
    cacheValidityPeriod: 30 * 1000, // 缓存有效期（毫秒），默认30秒
  };
  
  // 检查缓存是否有效
  static isCacheValid() {
    if (!this.stockCache.lastUpdateTime) return false;
    
    const now = Date.now();
    const timeSinceLastUpdate = now - this.stockCache.lastUpdateTime;
    return timeSinceLastUpdate < this.stockCache.cacheValidityPeriod;
  }
  
  // 更新所有股票数据
  static async updateAllStocks() {
    try {
      // 性能优化：检查是否可以使用缓存
      if (this.isCacheValid() && this.stockCache.data.size > 0) {
        console.log(`使用缓存数据，缓存时间: ${new Date(this.stockCache.lastUpdateTime).toLocaleTimeString()}，包含 ${this.stockCache.data.size} 只股票`);
        return Array.from(this.stockCache.data.values());
      }
      
      console.log(`缓存已过期或为空，开始获取最新股票数据...`);
      
      // 尝试从数据库获取所有股票
      let stocks = [];
      try {
        stocks = await Stock.getAllStocks();
      } catch (dbError) {
        console.warn('数据库连接失败，使用模拟股票数据:', dbError.message);
        // 使用模拟股票数据进行测试
        stocks = [
          { code: '000001', name: '平安银行' },
          { code: '600036', name: '招商银行' },
          { code: '000858', name: '五粮液' },
          { code: '600519', name: '贵州茅台' }
        ];
      }
      
      if (stocks.length === 0) {
        console.log('没有股票需要更新');
        return [];
      }
      
      // 提取所有股票代码
      const stockCodes = stocks.map(stock => {
        // 确保股票代码格式正确（添加sz或sh前缀）
        let formattedCode = stock.code;
        if (!formattedCode.startsWith('sz') && !formattedCode.startsWith('sh')) {
          // 简单判断股票代码类型
          if (formattedCode.startsWith('0') || formattedCode.startsWith('3') || formattedCode.startsWith('1')) {
            formattedCode = `sz${formattedCode}`;
          } else if (formattedCode.startsWith('6') || formattedCode.startsWith('5')) {
            formattedCode = `sh${formattedCode}`;
          }
        }
        return formattedCode;
      });
      
      try {
        // 性能优化：分批处理大量股票
        const batchSize = 10; // 每批处理的股票数量
        const stockDataResults = [];
        
        // 将股票代码分成多个批次
        for (let i = 0; i < stockCodes.length; i += batchSize) {
          const batchCodes = stockCodes.slice(i, i + batchSize);
          console.log(`处理第 ${Math.floor(i/batchSize) + 1}/${Math.ceil(stockCodes.length/batchSize)} 批，包含 ${batchCodes.length} 只股票`);
          
          // 使用tools.js中的batchGet方法批量获取股票数据
          const batchData = await StockTools.batchGet(batchCodes);
          stockDataResults.push(...batchData);
          
          // 如果不是最后一批，添加小延迟避免API限流
          if (i + batchSize < stockCodes.length) {
            await new Promise(resolve => setTimeout(resolve, 500));
          }
        }
        
        console.log(`已批量获取 ${stockDataResults.length} 只股票的数据`);
        
        // 更新缓存
        this.stockCache.data.clear();
        stockDataResults.forEach(stock => {
          this.stockCache.data.set(stock.code, stock);
        });
        this.stockCache.lastUpdateTime = Date.now();
        
        // 尝试保存到数据库
        try {
          // 性能优化：使用Promise.all并行处理数据库保存
          await Promise.all(stockDataResults.map(data => this.saveStockData(data)));
          console.log(`已批量更新 ${stockDataResults.length} 只股票的数据到数据库`);
        } catch (saveError) {
          console.warn('保存股票数据到数据库失败，但已成功获取数据:', saveError.message);
        }
        
        return stockDataResults;
      } catch (fetchError) {
        console.error('获取股票数据失败:', fetchError.message);
        return [];
      }
    } catch (error) {
      console.error('更新所有股票数据失败:', error);
      return [];
    }
  }
  
  // 保存股票数据到数据库
  static async saveStockData(stockData) {
    try {
      // 确保股票存在于数据库中，并提供默认名称防止undefined
      const stockName = stockData.name || `股票${stockData.code}`;
      const stockId = await Stock.addStock(stockData.code, stockName);
      
      // 添加价格记录
      await Stock.addStockPrice(
        stockId,
        stockData.price,
        stockData.openPrice,
        stockData.closePrice,
        stockData.highPrice,
        stockData.lowPrice,
        stockData.yesterdayClosePrice, // 昨日收盘价
        stockData.volume // 成交量
      );
      
      return true;
    } catch (error) {
      console.error(`保存股票 ${stockData.code} 数据到数据库失败:`, error);
      throw error;
    }
  }

  // 更新单个股票数据
  static async updateStockData(stockCode) {
    try {
      const stockData = await this.fetchStockData(stockCode);
      
      // 确保股票存在于数据库中，并提供默认名称防止undefined
      const stockName = stockData.name || `股票${stockCode}`;
      const stockId = await Stock.addStock(stockCode, stockName);
      
      // 添加价格记录
      await Stock.addStockPrice(
        stockId,
        stockData.price,
        stockData.openPrice,
        stockData.closePrice,
        stockData.highPrice,
        stockData.lowPrice,
        stockData.yesterdayClosePrice, // 昨日收盘价
        stockData.volume // 成交量
      );
      
      console.log(`股票 ${stockCode} 数据已更新`);
      return stockData;
    } catch (error) {
      console.error(`更新股票 ${stockCode} 数据失败:`, error);
      throw error;
    }
  }

  // 检查当前是否是交易时间
  static isTradingTime() {
    const now = new Date(); // 使用本地时间
    const day = now.getDay(); // 0是周日，1-5是周一到周五，6是周六
    const hours = now.getHours();
    const minutes = now.getMinutes();
    const currentTime = hours * 60 + minutes; // 转换为分钟计数
    
    // 检查是否是工作日（周一到周五）
    const isWorkday = day >= 1 && day <= 5;
    
    // 交易时间：9:29 - 15:00
    const marketOpenTime = 9 * 60 + 29; // 9:29
    const marketCloseTime = 15 * 60; // 15:00
    
    // 检查是否在交易时间内
    const isDuringTradingHours = currentTime >= marketOpenTime && currentTime <= marketCloseTime;
    
    return isWorkday && isDuringTradingHours;
  }
  
  // 获取下一个交易时间点
  static getNextTradingTime() {
    const now = new Date(); // 使用本地时间
    const day = now.getDay();
    const hours = now.getHours();
    const minutes = now.getMinutes();
    const currentTime = hours * 60 + minutes; // 转换为分钟计数
    
    // 交易时间：9:29 - 15:00
    const marketOpenTime = 9 * 60 + 29; // 9:29
    const marketCloseTime = 15 * 60; // 15:00
    
    let nextTime = new Date(now);
    
    // 如果当前是工作日
    if (day >= 1 && day <= 5) {
      // 如果当前时间早于开盘时间
      if (currentTime < marketOpenTime) {
        nextTime.setHours(9, 29, 0, 0);
        return nextTime;
      }
      
      // 如果当前时间晚于收盘时间，设置为下一个工作日的开盘时间
      if (currentTime > marketCloseTime) {
        // 如果是周五，则下一个交易日是下周一
        if (day === 5) {
          nextTime.setDate(nextTime.getDate() + 3); // 加3天到周一
        } else {
          nextTime.setDate(nextTime.getDate() + 1); // 加1天到下一个工作日
        }
        nextTime.setHours(9, 29, 0, 0);
        return nextTime;
      }
      
      // 如果当前时间在交易时间内，直接返回当前时间
      return nextTime;
    }
    
    // 如果当前是周末
    if (day === 0) { // 周日
      nextTime.setDate(nextTime.getDate() + 1); // 加1天到周一
    } else if (day === 6) { // 周六
      nextTime.setDate(nextTime.getDate() + 2); // 加2天到周一
    }
    
    nextTime.setHours(9, 29, 0, 0);
    return nextTime;
  }
  
  // 启动定时更新服务
  static startUpdateService(interval = 1000) {
    console.log(`股票数据更新服务已启动，更新间隔: ${interval}ms`);
    
    let timeoutId = null;
    let isRunning = true;
    
    // 定义更新函数
    const updateFunction = async () => {
      if (!isRunning) return;
      
      try {
        // 检查是否是交易时间
        if (this.isTradingTime()) {
          const now = new Date();
          console.log(`当前时间 ${now.toLocaleTimeString()} 在交易时间内，执行更新`);
          const startTime = Date.now();
          const stockData = await this.updateAllStocks();
          const endTime = Date.now();
          console.log(`更新完成，耗时 ${endTime - startTime}ms，获取到 ${stockData.length} 只股票数据`);
          
          // 设置下一次更新
          timeoutId = setTimeout(updateFunction, interval);
        } else {
          // 获取下一个交易时间
          const nextTradingTime = this.getNextTradingTime();
          const waitTime = nextTradingTime.getTime() - Date.now();
          
          const now = new Date();
          console.log(`当前时间 ${now.toLocaleTimeString()} 不在交易时间内`);
          console.log(`下一次更新将在 ${nextTradingTime.toLocaleString()} 进行，等待 ${Math.round(waitTime / 1000 / 60)} 分钟`);
          
          // 解决长时间setTimeout可能失效的问题
          // 如果等待时间超过1小时，则每30分钟检查一次
          const maxWaitTime = 30 * 60 * 1000; // 30分钟
          const actualWaitTime = Math.min(waitTime, maxWaitTime);
          
          if (waitTime > maxWaitTime) {
            console.log(`等待时间过长，将每30分钟检查一次交易时间状态`);
          }
          
          // 设置在下一个交易时间开始更新或定期检查
          timeoutId = setTimeout(updateFunction, actualWaitTime);
        }
      } catch (error) {
        console.error('股票数据更新过程中出错:', error);
        console.error('错误详情:', error.stack);
        
        // 即使出错，也继续下一次更新
        console.log(`出错后继续，${interval}ms 后重试...`);
        timeoutId = setTimeout(updateFunction, interval);
      }
    };
    
    // 立即执行第一次更新检查
    updateFunction();
    
    // 返回一个函数，用于停止更新服务
    return {
      stop: function() {
        console.log('股票数据更新服务已停止');
        isRunning = false;
        if (timeoutId) {
          clearTimeout(timeoutId);
          timeoutId = null;
        }
      }
    };
  }
}

module.exports = StockService;